﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace ICodeShare.UI.Controls.Assists
{
    /// <summary>
    /// 分组附加属性
    /// </summary>
    public static class GroupAssist
    {
        private static readonly Dictionary<MenuItem, string> menuElementToGroupNames;

        private static readonly Dictionary<ToggleButton, string> toggleElementToGroupNames;
        #region Constructors

        /// <summary>
        /// Initialises static members of the <see cref="GroupAssist"/> class.
        /// </summary>
        static GroupAssist()
        {
            menuElementToGroupNames = new Dictionary<MenuItem, string>();
            toggleElementToGroupNames = new Dictionary<ToggleButton, string>();
        } 

        #endregion

        #region GroupName 分组名称
        [AttachedPropertyBrowsableForType(typeof(MenuItem))]
        [AttachedPropertyBrowsableForType(typeof(ToggleButton))]
        public static string GetGroupName(DependencyObject element)
        {
            return element.GetValue(GroupNameProperty).ToString();
        }

        public static void SetGroupName(DependencyObject element, string value)
        {
            element.SetValue(GroupNameProperty, value);
        }

        public static readonly DependencyProperty GroupNameProperty = DependencyProperty.RegisterAttached(
         "GroupName",
         typeof(string),
         typeof(GroupAssist),
         new PropertyMetadata(null, OnGroupNameChanged)); 
        #endregion

        #region Private Static Methods

        /// <summary>
        /// Called when the group name is changed.
        /// </summary>
        /// <param name="dependencyObject">The dependency object.</param>
        /// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnGroupNameChanged(
            DependencyObject dependencyObject,
            DependencyPropertyChangedEventArgs e)
        {
            if (dependencyObject is MenuItem)
            {
                MenuItem menuItem = dependencyObject as MenuItem;

                string oldGroupName = e.OldValue == null ? null : e.OldValue.ToString();
                string newGroupName = e.NewValue == null ? null : e.NewValue.ToString();

                if (string.IsNullOrEmpty(newGroupName))
                {
                    RemoveMenuItemFromGroup(menuItem);
                }
                else
                {
                    if (!string.Equals(newGroupName, oldGroupName, StringComparison.Ordinal))
                    {
                        if (!string.IsNullOrEmpty(oldGroupName))
                        {
                            RemoveMenuItemFromGroup(menuItem);
                        }

                        AddMenuItemToGroup(menuItem, newGroupName);
                    }
                }
            }
            else if (dependencyObject is ToggleButton)
            {
                ToggleButton toggleButton = dependencyObject as ToggleButton;

                string oldGroupName = e.OldValue == null ? null : e.OldValue.ToString();
                string newGroupName = e.NewValue == null ? null : e.NewValue.ToString();

                if (string.IsNullOrEmpty(newGroupName))
                {
                    RemoveToggleButtonFromGroup(toggleButton);
                }
                else
                {
                    if (!string.Equals(newGroupName, oldGroupName, StringComparison.Ordinal))
                    {
                        if (!string.IsNullOrEmpty(oldGroupName))
                        {
                            RemoveToggleButtonFromGroup(toggleButton);
                        }

                        AddToggleButtonToGroup(toggleButton, newGroupName);
                    }
                }
            }
        }

        #region MenuItem

        /// <summary>
        /// Adds the menu item to the specified group.
        /// </summary>
        /// <param name="menuItem">The menu item.</param>
        /// <param name="groupName">Name of the group.</param>
        private static void AddMenuItemToGroup(MenuItem menuItem, string groupName)
        {
            menuElementToGroupNames.Add(menuItem, groupName);
            menuItem.Checked += OnMenuItemChecked;
        }

        /// <summary>
        /// Removes the menu item from it's group.
        /// </summary>
        /// <param name="menuItem">The menu item.</param>
        private static void RemoveMenuItemFromGroup(MenuItem menuItem)
        {
            menuElementToGroupNames.Remove(menuItem);
            menuItem.Checked -= OnMenuItemChecked;
        }

        /// <summary>
        /// Called when a menu item is checked.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="RoutedEventArgs"/> instance containing the event data.</param>
        private static void OnMenuItemChecked(object sender, RoutedEventArgs e)
        {
            MenuItem menuItem = (MenuItem)e.OriginalSource;

            string groupName = GetGroupName(menuItem);

            foreach (var item in menuElementToGroupNames
                .Where(item => !object.ReferenceEquals(item.Key, menuItem) && string.Equals(item.Value, groupName, StringComparison.Ordinal)))
            {
                item.Key.IsChecked = false;
            }
        }

        #endregion

        #region ToggleButton
        /// <summary>
        /// Adds the menu item to the specified group.
        /// </summary>
        /// <param name="menuItem">The menu item.</param>
        /// <param name="groupName">Name of the group.</param>
        private static void AddToggleButtonToGroup(ToggleButton toggleButton, string groupName)
        {
            toggleElementToGroupNames.Add(toggleButton, groupName);
            toggleButton.Checked += OnToggleButtonChecked;
        }

        /// <summary>
        /// Removes the menu item from it's group.
        /// </summary>
        /// <param name="menuItem">The menu item.</param>
        private static void RemoveToggleButtonFromGroup(ToggleButton toggleButton)
        {
            toggleElementToGroupNames.Remove(toggleButton);
            toggleButton.Checked -= OnToggleButtonChecked;
        }

        /// <summary>
        /// Called when a menu item is checked.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="RoutedEventArgs"/> instance containing the event data.</param>
        private static void OnToggleButtonChecked(object sender, RoutedEventArgs e)
        {
            ToggleButton toggleButton = (ToggleButton)e.OriginalSource;

            string groupName = GetGroupName(toggleButton);

            foreach (var item in toggleElementToGroupNames
                .Where(item => !object.ReferenceEquals(item.Key, toggleButton) && string.Equals(item.Value, groupName, StringComparison.Ordinal)))
            {
                item.Key.IsChecked = false;
            }
        }
        #endregion

        #endregion
    }
}
